/**
 * <h3>OpusLikeServiceImpl</h3>
 * <p> 作品点赞服务实现类 </p>
 *
 * <h3>功能描述</h3>
 * <p> 实现作品点赞相关的业务逻辑，包括点赞、取消点赞和点赞状态管理 </p>
 * <p> 通过事务管理确保点赞操作的原子性 </p>
 *
 * <h3>元数据</h3>
 * <ul>
 *    <li> 作者：郭强 </li>
 *    <li> 创建日期：2025年05月06日 </li>
 * </ul>
 *
 * <h3>其他信息</h3>
 * <ul>
 *    <li> 示例代码：{@code @Transactional} </li>
 * </ul>
 *
 * @apiNote 实现了幂等性处理，防止重复点赞
 * @implSpec 继承自 {@link ServiceImpl} 并实现 {@link IOpusLikeService} 接口
 * @since v1.0.0
 */
package com.dnndo.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dnndo.common.core.domain.R;
import com.dnndo.system.domain.OpusLike;
import com.dnndo.system.domain.dto.OpusDTO;
import com.dnndo.system.domain.dto.ThemeDTO;
import com.dnndo.system.mapper.OpusLikeMapper;
import com.dnndo.system.mapper.OpusMapper;
import com.dnndo.system.service.IOpusLikeService;
import com.dnndo.system.service.ThemeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service
public class OpusLikeServiceImpl extends ServiceImpl<OpusLikeMapper, OpusLike> implements IOpusLikeService   {

  private final OpusLikeMapper opusLikeMapper;

  private final OpusMapper opusMapper;
  private final ThemeService themeService;

  @Autowired
  public OpusLikeServiceImpl(OpusLikeMapper opusLikeMapper, OpusMapper opusMapper, ThemeService themeService) {
    this.opusLikeMapper = opusLikeMapper;
    this.opusMapper = opusMapper;
    this.themeService = themeService;
  }


  /**
   * 作品点赞/取消点赞操作（幂等处理）
   * 逻辑说明：
   * 1. 查询作品信息，若不存在直接返回失败。
   * 2. 构造点赞记录对象。
   * 3. 判断当前用户是否已点赞：
   *    - 已点赞：执行取消点赞（删除点赞记录，作品点赞数-1，更新作品），返回"已取消"。
   *    - 未点赞：执行点赞（作品点赞数+1，插入点赞记录，更新作品），返回"点赞成功"。
   * 4. 所有操作在事务中进行，确保原子性。
   * 5. 若任一步骤失败，返回相应失败信息。
   *
   * @param opusId 作品ID
   * @param userId 用户ID
   * @return R<String> 操作结果描述
   */
  @Override
  @Transactional
  public R<String> likeOpus(Long opusId, Long userId) {

    // 查询作品信息
    OpusDTO opusDTO = opusMapper.selectOpusListById(opusId);
    if (opusDTO == null) {
      return R.fail("作品不存在");
    }
    // 创建点赞记录实例
    OpusLike opusLike = new OpusLike();
    opusLike.setOpusId(Math.toIntExact(opusId));
    opusLike.setUserId(userId);
    // 判断是否已点赞
    if (exists(opusId, userId)) {
      // 已点赞，执行取消点赞
      int rows2 = opusLikeMapper.delete(opusLike);
      opusDTO.setTotalLikes(opusDTO.getTotalLikes() - 1);
      // 可选：同步修改积分
      // opusDTO = updateGrade(opusDTO, -0.01);
      int row3 = opusMapper.updateOpusById(opusDTO);
      if (rows2 <= 0 || row3 <= 0) {
        return R.ok("取消点赞失败");
      }
      return R.ok("已取消");
    }else{
      // 未点赞，执行点赞
      opusDTO.setTotalLikes(opusDTO.getTotalLikes() + 1);
      // 可选：同步修改积分
      // opusDTO = updateGrade(opusDTO, 0.01);
      // 更新作品点赞总数
      int rows0 = opusMapper.updateOpusById(opusDTO);
      // 保存点赞记录
      int rows1 = opusLikeMapper.insert(opusLike);
      if (rows0 < 0  || rows1 <= 0) {
        return R.ok("点赞记录保存失败");
      }
      return R.ok("点赞成功");
    }
  }

  /**
   * 判断用户是否已对指定作品点赞
   * 逻辑说明：
   * 1. 通过作品ID和用户ID联合查询点赞表，判断是否存在记录。
   * 2. 存在则返回true，否则返回false。
   *
   * @param opusId 作品ID
   * @param userId 用户ID
   * @return boolean 是否已点赞
   */
  public boolean exists(Long opusId, Long userId) {
    // 实际实现需根据数据库查询判断
    return count(new LambdaQueryWrapper<OpusLike>()
      .eq(OpusLike::getOpusId, opusId)
      .eq(OpusLike::getUserId, userId)) > 0;
  }

  /**
   * 修改作品积分（仅在比赛时间区间内生效）
   * 逻辑说明：
   * 1. 查询作品所属主题信息，判断当前时间是否在比赛区间。
   * 2. 若在区间内，按changeGrade调整积分。
   * 3. 否则不做任何修改。
   *
   * @param opusDTO 作品数据对象
   * @param changeGrade 积分变动值
   * @return OpusDTO 积分已调整的作品对象
   */
  public OpusDTO updateGrade(OpusDTO opusDTO,double changeGrade) {
    Integer themeId = opusDTO.getThemeId();
    // 判断当前时间是否在比赛时间区间
    ThemeDTO theme = themeService.selectThemeById(Long.valueOf(themeId));
    if (theme == null) {
      return opusDTO;
    }
    if (theme.getExpectStartTime().getTime() > System.currentTimeMillis() || theme.getOverTime().getTime() < System.currentTimeMillis()) {
      return opusDTO;
    }
    double grade = Double.parseDouble(opusDTO.getGrade());
    opusDTO.setGrade(String.valueOf(grade + changeGrade));
    return opusDTO;
  }
}
